home *** CD-ROM | disk | FTP | other *** search
- #!/bin/sh
- :; exec /usr/local/bin/stk -f "$0" "$@"
- ;;;
- ;;; 3D Tic-Tac-Toe
- ;;; written by Edin "Dino" Hodzic
- ;;; mailto:ehodzic@scu.edu
-
- ;;; last update: Jun 30, 1996.
-
- ;;; This is a free program written for STk 3.0.
-
- ;;; GUI related variables
-
- ; sizes and positions
- (define cell-width 15)
- (define cell-height 15)
- (define distance 15) ; vertical between planes
- (define x0 distance)
- (define y0 distance)
- (define modx0 0) ; actual x0
- (define mody0 0) ; actual y0
- (define angle 45) ; plane drawing angle
- (define delx 0) ; x change from row to row
- (define dely 0) ; y change from row to row
-
- ; colors
- (if (eq? (winfo 'visual '.) 'staticgray)
- (begin
- (define grid-color 'black)
- (define sign-color 'black)
- (define win-sign-color 'black)
- (define last-move-sign-color 'black)
- (define show-line-color 'black))
- (begin
- (define grid-color 'white)
- (define sign-color 'navy)
- (define win-sign-color 'red)
- (define last-move-sign-color 'blue)
- (define show-line-color 'magenta)))
-
- ;;; GUI related functions
-
- (define (set-angle! ang)
- (set! angle ang)
- (let* ((pi (* (atan 1) 4))
- (rangle (* angle (/ pi 180))))
- (set! delx (round (- (* cell-height (cos rangle)))))
- (set! dely (round (* cell-height (sin rangle))))))
-
- ; draw a 4x4 plane with x0 y0 reference point (in the canvas)
- (define (draw-plane x0 y0)
- (for-each-5
- (lambda (i)
- (let* ((rowx1 (+ x0 (* delx i)))
- (rowy1 (+ y0 (* dely i)))
- (rowx2 (+ rowx1 (* cell-width 4)))
- (rowy2 rowy1)
- (colx1 (+ x0 (* cell-width i)))
- (coly1 y0)
- (colx2 (+ colx1 (* delx 4)))
- (coly2 (+ y0 (* dely 4))))
- (.board 'create 'line ; horizontal line
- rowx1 rowy1
- rowx2 rowy2
- :fill grid-color
- :width 1)
- (.board 'create 'line ; vertical line
- colx1 coly1
- colx2 coly2
- :fill grid-color
- :width 1)
- (if (= i 4)
- (begin
- (.board 'create 'line ; plane bottom horizontal line
- rowx1 (+ 2 rowy1)
- rowx2 (+ 2 rowy2)
- :fill grid-color
- :width 2)
- (.board 'create 'line ; plane side vertical line
- colx1 (+ 2 coly1)
- colx2 (+ 2 coly2)
- :fill grid-color
- :width 2)))))))
-
- ; screen coordinates to point
- (define (screenxy->point x y)
- (let* ((canx (.board 'canvasx x))
- (cany (.board 'canvasy y))
- (x0 modx0)
- (y0 mody0)
- (plane (inexact->exact
- (floor (/ (- cany y0)
- (+ (* dely 4) distance)))))
- (rowq (/ (- cany y0 (+ (* dely 4 plane) (* distance plane)))
- dely))
- (row (inexact->exact (floor rowq)))
- (col (inexact->exact (floor
- (/ (- canx (+ x0 (* delx rowq)))
- cell-width)))))
- (if (and (<= 0 plane 3) (<= 0 row 3) (<= 0 col 3))
- (make-point plane row col)
- #f)))
-
- ; north-west corner of a point to board screen coordinates
- (define (point->screen point)
- (let ((plane (point-plane point))
- (row (point-row point))
- (col (point-column point)))
- (cons (inexact->exact (+ modx0 (* delx row)
- (* cell-width col)))
- (inexact->exact (+ mody0 (* plane dely 4)
- (* row dely) (* plane distance))))))
-
- ; center of a point to board screen coordinates
- (define (point->screen-center point)
- (let* ((scr (point->screen point))
- (x (inexact->exact (+ (car scr) (* cell-width 0.5) (* delx 0.5))))
- (y (inexact->exact (+ (cdr scr) (* dely 0.5)))))
- (cons x y)))
-
- ; the last point for which lines have been shown
- (define shown-lines-point #f)
-
- ; line showing flag
- (define show-lines-flag #f)
-
- (define (show-lines point)
- (if (and point (or (not shown-lines-point)
- (not (point-equal? point shown-lines-point))))
- (begin
- (set! shown-lines-point point)
- (.board 'delete "lines")
- (for-each
- (lambda (line)
- (let* ((first (line 0))
- (last (line 3))
- (scr-first (point->screen-center first))
- (scr-last (point->screen-center last)))
- (.board 'create 'line ; draw the line
- (car scr-first) (cdr scr-first)
- (car scr-last) (cdr scr-last)
- :tag "lines"
- :width 0
- :fill show-line-color)
- (for-each-point-in-line ; mark the point thru which line goes
- (lambda (point)
- (let ((scr (point->screen-center point)))
- (.board 'create 'text (car scr) (cdr scr)
- :text "="
- :tag "lines"
- :fill show-line-color)))
- line)))
- (point-lines point))))
- (if (not show-lines-flag)
- (.board 'delete "lines")))
-
- (define (toggle-show-lines point)
- (set! show-lines-flag (not show-lines-flag))
- (set! shown-lines-point #f)
- (note (string-append "Line showing is " (if show-lines-flag "ON" "OFF")))
- (show-lines point))
-
- ; show the move on the board
- (define (draw-move point val . high)
- (let* ((scr (point->screen-center point))
- (x (car scr))
- (y (cdr scr)))
- (.board 'delete (point->string point))
- (.board 'create 'text x y
- :text (value->sign val)
- :tag (point->string point)
- :fill (cond
- ((or (null? high) (eq? (car high) 'plain)) sign-color)
- ((equal? (car high) 'highlight) win-sign-color)
- ((equal? (car high) 'last) last-move-sign-color)
- (#t sign-color)))))
-
- (define (clear-move point)
- (.board 'delete (point->string point)))
-
- ; draw the 3D 4x4x4 board
- (define (draw-board)
- (catch (entry '.note :state 'disabled))
- (catch (canvas '.board :relief 'sunken :borderwidth 2))
- (catch
- (menubutton '.mb :text "File")
- (menu '.mb.m)
- (.mb.m 'add 'command :label "New Game" :command new-game)
- (.mb.m 'add 'command :label "Take Back" :command take-back-move)
- (.mb.m 'add 'command :label "Toggle Lines"
- :command (lambda () (toggle-show-lines #f)))
- (.mb.m 'add 'command :label "Resize" :command tune)
- (.mb.m 'add 'command :label "Quit" :command (lambda () (quit)))
- (.mb 'config :menu '.mb.m))
- (catch
- (menubutton '.help :text "Help")
- (menu '.help.m)
- (.help.m 'add 'command :label "About" :command about)
- (.help 'config :menu '.help.m))
- (.board 'delete 'all)
- (bind '.board "<1>" (lambda (x y) (action x y)))
- (bind '.board "<2>" take-back-move)
- (bind '.board "<3>" tune)
- (bind '.board "<Any-Motion>"
- (lambda (x y) (show-lines (screenxy->point x y))))
- (bind '.board "<l>"
- (lambda (x y) (toggle-show-lines (screenxy->point x y))))
- (bind '.board "<L>"
- (lambda (x y) (toggle-show-lines (screenxy->point x y))))
- (bind '. "<Q>" (lambda () (quit)))
- (bind '. "<q>" (lambda () (quit)))
- (bind '. "<space>" new-game)
- (bind '. "<m>" (lambda () (display moves) (newline)))
- (focus '.board)
- (for-each-4 ; draw plane
- (lambda (i)
- (draw-plane 0 (+ (* dely 4 i) (* distance i)))))
- (let* ((bbox (.board 'bbox 'all))
- (width (+ (- (caddr bbox) (car bbox)) x0 x0))
- (height (+ (- (cadddr bbox) (cadr bbox)) y0 y0)))
- (set! modx0 (- x0 (car bbox)))
- (set! mody0 (- y0 (cadr bbox)))
- (.board 'move 'all modx0 mody0)
- (.board 'configure :width width :height height)
- (pack '.note :expand #t :fill "x" :side 'bottom)
- (pack '.board :expand #t :fill "both" :side 'bottom)
- (pack '.mb :side 'left)
- (pack '.help :side 'right))
- (for-each-point1 ; draw point state sign
- (lambda (point)
- (let ((val (state-ref1 point)))
- (if (not (= val empty-value))
- (if (crosses? win-line point)
- (draw-move point val 'highlight)
- (if (or (and (not (null? moves))
- (point-equal? (car moves) point))
- (and (not (null? (cdr moves)))
- (point-equal? (cadr moves) point)))
- (draw-move point val 'last)
- (draw-move point val)))))))
- (let ((point shown-lines-point)) ; show lines if on
- (set! shown-lines-point #f)
- (show-lines point))
- (note ""))
-
- (define (about)
- (catch (destroy .about))
- (toplevel '.about)
- (message '.about.m
- :width 250
- :justify 'center
- :text
- "3D Tic-Tac-Toe\n\
- by\n\
- Edin \"Dino\" Hodzic\n\
- mailto:ehodzic@scu.edu")
- (button '.about.ok :text "OK" :command (lambda () (destroy '.about)))
- (pack '.about.m :expand #t :fill 'both :side 'top)
- (pack '.about.ok :side 'top))
-
- ; reset the board size/angle
- (define (tune-reset)
- (set! cell-width 15)
- (set! cell-height 15)
- (set-angle! 45))
-
- ; show the resizing window
- (define (tune)
- (catch (destroy '.tune))
- (toplevel '.tune)
- (scale '.tune.cw
- :label "Cell Width"
- :variable 'cell-width
- :command (lambda (v) (set-angle! angle) (draw-board))
- :relief 'sunken
- :orient 'horizontal)
- (scale '.tune.ch
- :label "Cell Height"
- :variable 'cell-height
- :command (lambda (v) (set-angle! angle) (draw-board))
- :relief 'sunken
- :orient 'horizontal)
- (scale '.tune.an
- :label "Angle"
- :variable 'angle
- :orient 'horizontal
- :relief 'sunken
- :to 180
- :command (lambda (v) (set-angle! angle) (draw-board)))
- (button '.tune.quit :text "Close"
- :command (lambda () (destroy '.tune)))
- (button '.tune.reset :text "Reset"
- :command (lambda () (tune-reset) (draw-board)))
- (pack '.tune.cw :fill 'x :side 'top)
- (pack '.tune.ch :fill 'x :side 'top)
- (pack '.tune.an :fill 'x :side 'top)
- (pack '.tune.reset :fill 'x :side 'left)
- (pack '.tune.quit :fill 'x :side 'right))
-
- ; highlight the winning line
- (define (show-winning move)
- (let ((val (state-ref1 move)))
- (for-each-point-in-line ; draw highlighed point
- (lambda (point)
- (draw-move point val 'highlight))
- win-line)
- (note (string-append
- "Game Over - "
- (if (= val comp-value)
- "I win!"
- "You win!")))))
-
- ; show a note
- (define (note str)
- (.note 'config :state 'normal)
- (.note 'delete 0 'end)
- (.note 'insert 0 str)
- (.note 'config :state 'disabled))
-
- ; clear up everything and restart the game
- (define (new-game)
- (clear-state!)
- (set! game-over #f)
- (set! win-line #f)
- (set! moves '())
- (draw-board))
-
- ;;; state and other game related variables
-
-
- (define signs (vector #f "o" "x")) ; player signs
- (define game-over #f) ; game over flag
- (define win-line #f) ; the winning line
- (define empty-value 0) ; available point value in the state
- (define user-value 1) ; user value in the state vector
- (define comp-value 2) ; compute value in the state vector
- (define state #f) ; the board state
- (define moves '()) ; the list of all moves played
-
- ;;; state access functions
-
- (define (value->sign val)
- (vector-ref signs val))
-
- (define (state-ref p r c)
- (let ((ind (inexact->exact (+ c (* 4 (+ r (* 4 p)))))))
- (vector-ref state ind)))
-
- (define (state-ref1 point)
- (state-ref (point-plane point)
- (point-row point)
- (point-column point)))
-
- (define (state-set! p r c v)
- (let ((ind (inexact->exact (+ c (* 4 (+ r (* 4 p)))))))
- (vector-set! state ind v)))
-
- (define (state-set1! point val)
- (state-set! (point-plane point)
- (point-row point)
- (point-column point) val))
-
- (define (clear-state!)
- (set! state (make-vector 64 empty-value)))
-
- ;;; playing functions
-
- ; entry point on user click
- (define (action x y)
- (let* ((user-move (screenxy->point x y)))
- (if (and (not game-over) user-move)
- (play user-move))))
-
- (define (acceptable-move? move)
- (and (not game-over)
- (= (state-ref1 move) empty-value)))
-
- ; play with user's move
- (define (play move)
- (if (acceptable-move? move)
- (begin
- (note "")
- (enter-move move user-value)
- (if (won? move)
- (show-winning move)
- (let ((comp-move (make-move)))
- (enter-move comp-move comp-value)
- (if (won? comp-move)
- (show-winning comp-move)
- (if (draw?)
- (note "It's a draw!"))))))
- (note "Bad move")))
-
- ; change the state and draw the move on the board
- (define (enter-move move val)
- (if move
- (begin
- (state-set1! move val)
- (set! moves (cons move moves))
- (if (and
- (not (null? (cdr moves)))
- (not (null? (cddr moves))))
- (begin
- (clear-move (caddr moves))
- (draw-move (caddr moves) val 'plain)))
- (draw-move move val 'last)
- (update 'idletasks))))
-
- ; take a move back
- (define (take-back-move)
- (if (null? moves)
- (note "No more moves")
- (let ((take-one
- (lambda ()
- (if (not (null? moves))
- (begin
- (state-set1! (car moves) empty-value)
- (clear-move (car moves))
- (set! moves (cdr moves)))))))
- (take-one)
- (if (= (state-ref1 (car moves)) user-value)
- (take-one)) ; one more for the user move
- (set! game-over #f)
- (set! win-line #f)
- (draw-board))))
-
- ; deciding among same score moves
- (define (doexchange?)
- (= (random 5) 0))
-
- ; find the best move
- (define (make-move)
- (let ((best-score -1)
- (best-move #f))
- (for-each-point1
- (lambda (point)
- (if (= (state-ref1 point) empty-value)
- (let ((score (score-if-played point)))
- (if (or (> score best-score)
- (and (= score best-score) (doexchange?)))
- (begin
- (set! best-score score)
- (set! best-move point)))))))
- best-move))
-
- ; position evaluation
- (define (score-if-played point)
- (let ((score 0))
- (for-each
- (lambda (line)
- (let ((user-count 0)
- (comp-count 1))
- (for-each-point-in-line
- (lambda (point)
- (let ((val (state-ref1 point)))
- (cond
- ((= val comp-value)
- (set! comp-count (1+ comp-count)))
- ((= val user-value)
- (set! user-count (1+ user-count))))))
- line)
- (cond ((= user-count 0) ; offensive
- (begin
- (set! score (+ score (vector-ref
- #(0 80 100 2000 100000)
- comp-count)))
- (if (= comp-count 2)
- (for-each-point-in-line
- (lambda (p)
- (if (and (= (state-ref1 p) empty-value)
- (not (point-equal? point p)))
- (for-each
- (lambda (line)
- (let ((user-count 0)
- (comp-count 0))
- (for-each-point-in-line
- (lambda (point)
- (let ((val (state-ref1 point)))
- (cond
- ((= val comp-value)
- (set! comp-count (1+ comp-count)))
- ((= val user-value)
- (set! user-count
- (1+ user-count))))))
- line)
- (if (and (= user-count 0)
- (>= comp-count 2))
- (set! score (+ score 800)))))
- (point-lines p))))
- line))))
- ((= comp-count 1) ; defensive
- (set! score (+ score (vector-ref
- #(0 50 1500 10000)
- user-count)))))))
- (point-lines point))
- score))
-
- ; checking if the game is won
- (define (won? move)
- (let ((val (state-ref1 move)))
- (for-each
- (lambda (line)
- (if (full-line? line val)
- (set! win-line line)))
- (point-lines move)))
- (set! game-over win-line)
- game-over)
-
- ; is it a draw?
- (define (draw?)
- (= (length moves) 64))
-
- ;;; point functions
-
- (define (make-point p r c)
- (vector p r c))
-
- (define (point-equal? p1 p2)
- (and (= (point-plane p1) (point-plane p2))
- (= (point-row p1) (point-row p2))
- (= (point-column p1) (point-column p2))))
-
- (define (point-plane point)
- (vector-ref point 0))
-
- (define (point-row point)
- (vector-ref point 1))
-
- (define (point-column point)
- (vector-ref point 2))
-
- (define (point->string point)
- (string-append
- (number->string (point-plane point))
- ","
- (number->string (point-row point))
- ","
- (number->string (point-column point))))
-
- ;;; line functions
-
- (define (complement x)
- (- 3 x))
-
- ; line templates:
- ; car is the predicate for the line to go through a point.
- ; cdr is the line.
- (define line-tpl
- (list
- (cons
- (lambda (p r c) #t)
- (lambda (p r c) (lambda (x) (make-point p r x))))
- (cons
- (lambda (p r c) #t)
- (lambda (p r c) (lambda (x) (make-point p x c))))
- (cons
- (lambda (p r c) #t)
- (lambda (p r c) (lambda (x) (make-point x r c))))
- (cons
- (lambda (p r c) (= r c))
- (lambda (p r c) (lambda (x) (make-point p x x))))
- (cons
- (lambda (p r c) (= r (complement c)))
- (lambda (p r c) (lambda (x) (make-point p x (complement x)))))
- (cons
- (lambda (p r c) (= p c))
- (lambda (p r c) (lambda (x) (make-point x r x))))
- (cons
- (lambda (p r c) (= p (complement c)))
- (lambda (p r c) (lambda (x) (make-point x r (complement x)))))
- (cons
- (lambda (p r c) (= p r))
- (lambda (p r c) (lambda (x) (make-point x x c))))
- (cons
- (lambda (p r c) (= p (complement r)))
- (lambda (p r c) (lambda (x) (make-point x (complement x) c))))
- (cons
- (lambda (p r c) (= p r c))
- (lambda (p r c) (lambda (x) (make-point x x x))))
- (cons
- (lambda (p r c) (= p r (complement c)))
- (lambda (p r c) (lambda (x) (make-point x x (complement x)))))
- (cons
- (lambda (p r c) (= p (complement r) c))
- (lambda (p r c) (lambda (x) (make-point x (complement x) x))))
- (cons
- (lambda (p r c) (= (complement p) r c))
- (lambda (p r c) (lambda (x) (make-point (complement x) x x))))))
-
- ; list of lines going through a point
- (define (point-lines point)
- (let ((plane (point-plane point))
- (row (point-row point))
- (column (point-column point))
- (lines '()))
- (for-each
- (lambda (tpl)
- (if ((car tpl) plane row column)
- (set! lines
- (cons
- ((cdr tpl) plane row column)
- lines))))
- line-tpl)
- lines))
-
- ; whther line crosses point
- (define (crosses? line point)
- (if line
- (let ((crosses?
- (lambda (return)
- (for-each-point-in-line
- (lambda (line-point)
- (if (point-equal? line-point point)
- (return #t)))
- line)
- (return #f))))
- (call/cc crosses?))
- #f))
-
- ;;; iterators
-
- (define (for-each-4 func)
- (for-each func '(0 1 2 3)))
-
- (define (for-each-5 func)
- (for-each func '(0 1 2 3 4)))
-
- ; each point on the board (func plane row col)
- (define (for-each-point func)
- (for-each-4
- (lambda (plane)
- (for-each-4
- (lambda (row)
- (for-each-4
- (lambda (col)
- (func plane row col))))))))
-
- ; similar to the above (func point)
- (define (for-each-point1 func)
- (for-each-point
- (lambda (plane row col)
- (func (make-point plane row col)))))
-
- ; for each point in a line call (func point)
- (define (for-each-point-in-line func line)
- (for-each-4
- (lambda (x)
- (func (line x)))))
-
- ; is the line full of value
- (define (full-line? line value)
- (let ((full?
- (lambda (return)
- (for-each-point-in-line
- (lambda (point)
- (if (not (= (state-ref1 point) value))
- (return #f)))
- line)
- (return #t))))
- (call/cc full?)))
-
- ;;; game initiation
-
- (tune-reset)
- (new-game)
-